package com.mall.service.impl;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mall.common.exception.BizIllegalException;
import com.mall.common.utils.BeanUtils;
import com.mall.common.utils.UserContext;
import com.mall.domain.dto.PayApplyDTO;
import com.mall.domain.dto.PayOrderFormDTO;
import com.mall.domain.po.Order;
import com.mall.domain.po.PayOrder;
import com.mall.enums.PayStatus;
import com.mall.enums.PayType;
import com.mall.mapper.PayOrderMapper;
import com.mall.service.IOrderService;
import com.mall.service.PayOrderService;
import com.mall.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements PayOrderService {

    private final UserService userService;

    private final IOrderService orderService;

    @Override
    public String applyPayOrder(PayApplyDTO payApplyDTO) {
        //1.幂等性校验
        PayOrder payOrder=checkIdempotent(payApplyDTO);
        //2.返回结果
        return payOrder.getId().toString();
    }



    @Override
    @Transactional
    public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
        //1.查询支付单
        PayOrder po = getById(payOrderFormDTO.getId());
        //2.判断订单状态
        if (!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())){
            // 订单不是未支付状态，状态异常
            throw new BizIllegalException("交易已支付或关闭！");
        }
        //3.尝试扣减用户余额
        userService.deductMoney(payOrderFormDTO.getPw(),po.getAmount());
        //4.修改支付状态
        boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
        //5.修改订单状态
        Order order = new Order();
        order.setId(po.getBizOrderNo());
        order.setStatus(2);
        order.setPayTime(LocalDateTime.now());
        orderService.updateById(order);
    }

    private boolean markPayOrderSuccess(@NotNull(message = "支付订单id不能为空") Long id, LocalDateTime now) {
        return lambdaUpdate()
                .set(PayOrder::getStatus, PayStatus.TRADE_SUCCESS.getValue())
                .set(PayOrder::getPaySuccessTime,now)
                // 支付状态的乐观锁判断，有当订单当前状态为"未提交"( NOT_COMMIT )或"待买家付款"( WAIT_BUYER_PAY )时，才执行更新操作。
                .in(PayOrder::getStatus,PayStatus.NOT_COMMIT.getValue(),PayStatus.WAIT_BUYER_PAY.getValue())
                .update();

    }

    /**
     * 确保重复请求不会创建重复订单
     * @param payApplyDTO
     * @return
     */
    private PayOrder checkIdempotent(PayApplyDTO payApplyDTO) {
        //1.首先查询支付订单
        PayOrder oldOrder=lambdaQuery().
                eq(PayOrder::getBizOrderNo,payApplyDTO.getBizOrderNo())
                .one();
        //2.判断当前提交的支付订单是否已经存在
        if (oldOrder == null) {
            PayOrder payOrder = buildPayOrder(payApplyDTO);
            payOrder.setPayOrderNo(IdWorker.getId());
            save(payOrder);
            return payOrder;
        }
        //3.已经存在这个支付订单，判断是否支付成功
        if (PayStatus.TRADE_SUCCESS.equalsValue(oldOrder.getStatus())){
            // 已经支付成功，抛出异常
            throw new BizIllegalException("订单已经支付！");
        }
        //4.已经存在这个支付订单，判断订单是否已经关闭
        if (PayStatus.TRADE_CLOSED.equalsValue(oldOrder.getStatus())){
            // 已经支付成功，抛出异常
            throw new BizIllegalException("订单已关闭！");
        }
        //5.已经存在这个支付订单，判断当前提交的新订单与旧订单的支付渠道是否一致
        if (!StringUtils.equals(oldOrder.getPayChannelCode(),payApplyDTO.getPayChannelCode())){
            // 支付渠道不一致，需要重置数据，然后重新申请支付单
            PayOrder payOrder = buildPayOrder(payApplyDTO);
            payOrder.setId(oldOrder.getId());
            payOrder.setQrCodeUrl("");
            updateById(payOrder);
            payOrder.setPayOrderNo(oldOrder.getPayOrderNo());
            return payOrder;
        }
        //6.旧单已经存在，且可能是未支付或未提交，且支付渠道一致，直接返回旧数据
        return oldOrder;
    }

    /**
     *构造支付订单
     * @param payApplyDTO
     * @return
     */
    private PayOrder buildPayOrder(PayApplyDTO payApplyDTO) {
        // 1.数据转换,payApplyDTO转为payOrder
        PayOrder payOrder = BeanUtils.toBean(payApplyDTO, PayOrder.class);
        // 2.初始化payOrder数据
        payOrder.setPayOverTime(LocalDateTime.now().plusMinutes(120L));
        payOrder.setStatus(PayStatus.WAIT_BUYER_PAY.getValue());
        payOrder.setBizUserId(UserContext.getUser());
        return payOrder;

    }
}
